#include "Drv_SSD1306.h"
#include "Font.h"
#include "BitBand.h"


// 引脚定义
#define OLED_D0   PBout(15)
#define OLED_D1   PDout(10)
#define OLED_RES  PDout(8)
#define OLED_DC   PEout(14)
#define OLED_CS   PEout(12)


// 通讯协议
#define USING_IIC_HAED     0
#define USING_IIC_GPIOSIMU 0
#define USING_SPI_HARD     0
#define USING_SPI_GPIOSIMU 1

#define REGCMD      0x00
#define REGDATA     0x40
#define OLED_ADDR   0x78
#define OLED_CMD    0
#define OLED_DATA   1
#define OLED_WIDTH  128
#define OLED_HIGH   64
#define BRIGHT      0x01
#define DARK        0x00

#define OLED_Reset()  {OLED_RES = 0; HAL_Delay(100); OLED_RES = 1;}


uint8_t OLED_GRAM[128][8];


void OLED_WriteByte(uint8_t DC, uint8_t byte)
{
  uint32_t i;

#if USING_IIC_HAED
// todo
#elif USING_IIC_GPIOSIMU
// todo
#elif USING_SPI_HARD
// todo
#elif USING_SPI_GPIOSIMU
  OLED_DC = DC;
  OLED_CS = 0;
  for(i=0; i<8; ++i)
  {
    OLED_D0 = 0;
    OLED_D1 = (byte &0x80) ?1 :0;
    OLED_D0 = 1;
    byte <<= 1;
  }
  OLED_CS = 1;
  OLED_DC = 1;
#endif
}

void OLED_Init()
{
  OLED_Reset();

#define InitCMD
#ifdef InitCMD
  OLED_WriteByte(OLED_CMD, 0xAE);//0xAE:关显示   
  OLED_WriteByte(OLED_CMD, 0x00);//设置低列地址
  OLED_WriteByte(OLED_CMD, 0x10);//设置高列地址
  //设置行显示的开始地址(0-63)
  //40-47: (01xxxxx)
  OLED_WriteByte(OLED_CMD, 0x40);
  //设置对比度
  OLED_WriteByte(OLED_CMD, 0x81);
  OLED_WriteByte(OLED_CMD, 0xff);//这个值越大，屏幕越亮(和上条指令一起使用)(0x00-0xff)
  OLED_WriteByte(OLED_CMD, 0xA1);//0xA1: 左右反置，  0xA0: 正常显示（默认0xA0）
  OLED_WriteByte(OLED_CMD, 0xC8);//0xC8: 上下反置，  0xC0: 正常显示（默认0xC0）
  //0xA6: 表示正常显示（在面板上1表示点亮，0表示不亮）
  //0xA7: 表示逆显示（在面板上0表示点亮，1表示不亮）
  OLED_WriteByte(OLED_CMD, 0xA6);
  OLED_WriteByte(OLED_CMD, 0xA8);//设置多路复用率（1-64）
  OLED_WriteByte(OLED_CMD, 0x3F);//（0x01-0x3f）(默认为3f)
  //设置显示抵消移位映射内存计数器
  OLED_WriteByte(OLED_CMD, 0xD3);
  OLED_WriteByte(OLED_CMD, 0x00);//（0x00-0x3f）(默认为0x00)
  //设置显示时钟分频因子/振荡器频率
  OLED_WriteByte(OLED_CMD, 0xD5);
  //低4位定义显示时钟(屏幕的刷新时间)（默认：0000）分频因子= [3:0]+1
  //高4位定义振荡器频率（默认：1000）
  OLED_WriteByte(OLED_CMD, 0x80);//
  //时钟预充电周期
  OLED_WriteByte(OLED_CMD, 0xD9);
  OLED_WriteByte(OLED_CMD, 0xF1);//[3:0],PHASE 1;   [7:4] PHASE 2
  //设置COM硬件应脚配置
  OLED_WriteByte(OLED_CMD, 0xDA);
  OLED_WriteByte(OLED_CMD, 0x12);//[5:4]  默认：01
  OLED_WriteByte(OLED_CMD, 0xDB);//
  OLED_WriteByte(OLED_CMD, 0x40);//
  //设置内存寻址方式
  OLED_WriteByte(OLED_CMD, 0x20);
  //00: 表示水平寻址方式
  //01: 表示垂直寻址方式
  //10: 表示页寻址方式（默认方式）
  OLED_WriteByte(OLED_CMD, 0x02);//    
  //电荷泵设置（初始化时必须打开，否则看不到显示）
  OLED_WriteByte(OLED_CMD, 0x8D);
  OLED_WriteByte(OLED_CMD, 0x14);//bit2   0：关闭        1：打开
  //设置是否全部显示 0xA4: 禁止全部显示
  OLED_WriteByte(OLED_CMD, 0xA4);
  //0xA6: 表示正常显示（在面板上1表示点亮，0表示不亮）
  //0xA7: 表示逆显示（在面板上0表示点亮，1表示不亮）
  OLED_WriteByte(OLED_CMD, 0xA6);//
  OLED_WriteByte(OLED_CMD, 0xAF);//0xAF:开显示
#endif
  OLED_Clear(BRIGHT);
  HAL_Delay(1000);
  OLED_Clear(DARK);
}

void OLED_DisplayOn()
{
  OLED_WriteByte(OLED_CMD, 0x8D); // 电荷泵设置
  OLED_WriteByte(OLED_CMD, 0x14); // bit2 0:off 1:0n
  OLED_WriteByte(OLED_CMD, 0xAF); // 开启显示
}

void OLED_DisplayOff()
{
  OLED_WriteByte(OLED_CMD, 0x8D); // 电荷泵设置
  OLED_WriteByte(OLED_CMD, 0x10); // bit2 0:off 1:0n
  OLED_WriteByte(OLED_CMD, 0xAE); // 关闭显示
}

void OLED_RefreshGRAM()
{
  uint32_t i_page, j_seg;

  for(i_page = 0; i_page < 8; ++i_page)
  {
    OLED_WriteByte(OLED_CMD, 0xB0+i_page);  // 设置页地址(0~7)
    OLED_WriteByte(OLED_CMD, 0x00);         // 设置列低地址为0
    OLED_WriteByte(OLED_CMD, 0x10);         // 设置列高地址为0
    for(j_seg = 0; j_seg < 128; ++j_seg)
      OLED_WriteByte(OLED_DATA, OLED_GRAM[j_seg][i_page]);
  }
}

void OLED_Clear(uint8_t state)
{
  uint8_t *pGRAM;
  int32_t i;

  pGRAM = (uint8_t*)OLED_GRAM;
  for(i = 0; i < 128*8; ++i)
    *(pGRAM+i) = (state) ?0xFF :0x00;
  OLED_RefreshGRAM();
}

void GRAM_DrawPoint(uint8_t x, uint8_t y, uint8_t state)
{
  if((x>127) |(y>63))
    return;
  
  uint8_t tmp, page;

  page = y/8;
  tmp = 1 << (y%8);
  if(state)
    OLED_GRAM[x][page] |= tmp;
  else
    OLED_GRAM[x][page] &= ~tmp; 
}

void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t fontSize)
{
  uint32_t i, j, tmp, yHead;

  yHead = y;
  chr = chr - ' ';
  for(i = 0; i < (fontSize/8 + (fontSize%8 ?1 :0))*(fontSize/2); ++i)
  {
    if(fontSize == 12) tmp = asc2_1206[chr][i];
    else if(fontSize == 16) tmp = asc2_1608[chr][i];
    else if(fontSize == 24) tmp = asc2_2412[chr][i];
    else return;
    for(j = 0; j < 8; ++j)
    {
      if((x < OLED_WIDTH) &&(y < OLED_HIGH))
        GRAM_DrawPoint(x, y, tmp & 0x80);
      tmp <<= 1;
      ++y;
      if(y - yHead == fontSize)
      {
        y = yHead;
        ++x;
        break;
      }
    }
  }
}

void OLED_ShowString(uint8_t x, uint8_t y, char* Pstr, uint8_t lenth, uint8_t fontSize)
{
  uint32_t i=0;

  while((*Pstr != '\0') && (i < lenth - 1))
  {
    // 一行显示不下换行
//    if(x >= OLED_WIDTH - (fontSize/2))
//    {
//      x = 0;
//      y += fontSize;
//    }
    // 一页显示不下换页
//    if(y >= OLED_HIGH - fontSize)
//    {
//      x = y = 0;
//    }

    if((*Pstr >= ' ')&&(*Pstr <= '~'))
      OLED_ShowChar(x, y, *Pstr, fontSize);
    else
      OLED_ShowChar(x, y, '*', fontSize);
    x += fontSize/2;
    Pstr++;
    i++;
  }
  OLED_RefreshGRAM();
}